<?php
/**
 * @brief   加密,解密类
 * @note    加密,解密类
 * @author  huangfeng
 * @version 1.0
 * @since   2024-6-7
 */
namespace app\common;

/**
 * @brief   加密,解密类
 * @note    加密,解密类
 * @author  huangfeng
 * @since   2024-6-7
 */
class Crypt
{
    /**
     * @brief  加密密钥
     * @note   加密密钥
     * @author huangfeng
     * @date   2024-6-7
     */
    private static $sDbKey = 'helloworldjinglem';

    /**
     * @brief 验证签名
     * @note  验证签名
     * @author huangfeng
     *
     * @param array $p_aParams 数组中包含加密用数据 + 'key' => kval(系统对应存储的单一key值) + 'sign' => sval(用于验证的传过来的sign值)
     * @param int $p_iType 加密类型
     *
     * @date 2024-6-7
     * @return bool
     */
    public static function verifySign($p_aParams, $p_iType)
    {
        $sSign = $p_aParams['sign'];
        unset($p_aParams['sign']);
        if (0 !== strcmp($sSign, self::createSign($p_aParams, $p_iType))){
            return false;
        }

        return true;
    }

    /**
     * @brief 创建系统验证签名
     * @note  创建系统验证签名
     * @author huangfeng
     *
     * @param array $p_aParams 需要加密的键值数组
     * @param int $p_iType 加密方式
     *
     * @date 2024-6-7
     * @return string 用户登录加密串
     */
    public static function createSign($p_aParams, $p_iType)
    {
        $key = isset($p_aParams['key']) ? $p_aParams['key'] : '';
        unset($p_aParams['key']);
        switch($p_iType)
        {
            case 1:  //所有参数顺排, 构造的字符串和KEY进行一次MD5加密
                return self::getMd5OnceSign($p_aParams, $key);
                break;
            case 2:  //所有参数顺排, 构造的字符串进行一次MD5加密, 然后和KEY再进行一次MD5加密
                return self::getMd5ParamsKeySign($p_aParams, $key);
                break;
            case 3:  //所有参数顺排, 通过http_build_query构造的字符串和KEY进行一次MD5加密之后再进行一次MD5加密
                return self::getHttpBuildQuery($p_aParams, $key);
                break;
            case 4:  //输入参数进行json_encode，然后再连当前时间（精确到小时），进行hash加密，最后md5加密连接接口加密参数
            	return self::getMd5JsonParamsSignKey($p_aParams['jsondata'], $key);
            	break;
        }
    }

    /**
     * @brief 输入参数进行json_encode，然后再连当前时间（精确到小时），进行hash加密，最后md5加密连接接口加密参数
     * @note 输入参数进行json_encode，然后再连当前时间（精确到小时），进行hash加密，最后md5加密连接接口加密参数
     * @param string $p_sJsonParam 数据json串
     * @param string $p_sKey  秘钥
     * @return string 加密串
     * @author huangfeng
     * @since 2024-6-7
     */
    private static function getMd5JsonParamsSignKey($p_sJsonParam, $p_sKey)
    {
    	return md5( hash( 'sha256' , $p_sJsonParam . date( 'YmdH' ) . $p_sKey ) );
    }

    /** 
     * @brief 所有参数顺排, 构造的字符串和KEY进行一次MD5加密
     * @note  所有参数顺排, 构造的字符串和KEY进行一次MD5加密
     * @author huangfeng
     * 
     * @param array $p_aInput 需要加密的数组列表
     * @param string $p_sKey  秘钥
     * 
     * @date 2024-6-7
     * @return string 加密串
     */
    private static function getMd5OnceSign($p_aInput, $p_sKey)
    {
        ksort($p_aInput);
        $sSign = '';
        foreach ($p_aInput as $value) {
            $sSign .= $value;
        }
        return md5($sSign . $p_sKey);
    }


    /** 
     * @brief 所有参数顺排, 构造的字符串进行一次MD5加密, 然后和KEY再进行一次MD5加密
     * @note  所有参数顺排, 构造的字符串进行一次MD5加密, 然后和KEY再进行一次MD5加密
     * @author huangfeng
     * 
     * @param array $p_aInput 需要加密的数组列表
     * @param string $p_sKey  秘钥
     * 
     * @date 2024-6-7
     * @return string 加密串
     */
    private static function getMd5ParamsKeySign($p_aInput, $p_sKey)
    {
        ksort($p_aInput);
        $sSign = '';
        foreach ($p_aInput as $value) {
            $sSign .= $value;
        }
        return md5(md5($sSign) . $p_sKey);
    }

    /** 
     * @brief 所有参数顺排, 通过http_build_query构造的字符串和KEY进行一次MD5加密之后再进行一次MD5加密
     * @note  所有参数顺排, 通过http_build_query构造的字符串和KEY进行一次MD5加密之后再进行一次MD5加密
     * @author huangfeng
     * 
     * @param array $p_aInput 需要加密的数组列表
     * @param string $p_sKey  秘钥
     * 
     * @date 2024-6-7
     * @return string 加密串
     */
    private static function getHttpBuildQuery($p_aInput, $p_sKey)
    {
        ksort($p_aInput);
        return md5(md5(http_build_query($p_aInput)) . $p_sKey);
    }

    /**
     * @brief RSA签名
     * @note  RSA签名
     * 
     * @author huangfeng
     * 
     * @date 2024-6-7
     * @return string $sSign 签名
     */
    public static function selectRSASign($p_sSignContent, $p_sKey)
    {
        $sRes = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($p_sKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        openssl_sign($p_sSignContent, $sSign, $sRes, OPENSSL_ALGO_SHA256);
        $sSign = base64_encode($sSign);
        return $sSign;
    }

    /**
     * @brief 获取签名内容
     * @note  获取签名内容
     * 
     * @author huangfeng
     * 
     * @date 2024-6-7
     * @return string 签名内容
     */
    public static function selectSignContent($p_aRequest)
    {
        unset($p_aRequest['sign']);
        ksort($p_aRequest);
        $sSignContent = '';
        foreach ($p_aRequest as $sKey => $mValue){
            $sSignContent .= $sKey . '=' . $mValue . '&';
        }
        //删除最后一个'&'
        return rtrim($sSignContent, '&');
    }

    /**
     * @brief 密码加密方法
     * @note 密码加密方法
     *
     * @author huangfeng
     *
     * @param string $p_sText 需要加密的字符串
     * @param string $p_sKey 加密密钥
     *
     * @date 2024-6-7
     * @return string
     */
    public static function encrypt($p_sText,$p_sKey = '')
    {
        $p_sText = rawurlencode($p_sText);
        $key = $p_sKey!='' ? $p_sKey : self::$sDbKey;

        mt_srand((double)microtime() * 1000000);
        $strkey = md5(mt_rand(0,32000));
        $ctr = 0;
        $tmp = '';
        for($i = 0;$i<strlen($p_sText);$i++){
            $ctr = $ctr == strlen($strkey) ? 0 : $ctr;
            $tmp .= $strkey[$ctr].($p_sText[$i]^$strkey[$ctr++]);
        }
        return base64_encode(self::__key($tmp, $key));
    }

    /**
     * @brief 密码解密方法
     * @note 密码解密方法
     *
     * @author huangfeng
     *
     * @param string $p_sText 需要加密的字符串
     * @param string $p_sKey 加密密钥
     *
     * @date 2024-6-7
     * @return string
     */
    public static function decrypt($p_sText,$p_sKey = '')
    {
        $key = $p_sKey!='' ? $p_sKey : self::$sDbKey;
        $p_Text = self::__key(base64_decode($p_sText),$key);
        $tmp = '';
        if (strlen($p_Text) % 2== 0){
            for($i = 0;$i < strlen($p_Text); $i++){
                $md5 = $p_Text[$i];
                $tmp .= $p_Text[++$i] ^ $md5;
            }
        }
        return rawurldecode($tmp);
    }

    /**
     * @brief 处理字符串
     *
     * @author huangfeng
     * @date 2018-03-20
     * @param string $p_sText 需要加密/解密的字符串
     * @param string $p_sKey key值
     *
     * @date 2024-6-7
     * @return string
     */
    public static function __key($p_sText,$p_sKey)
    {
        $p_strKey = md5($p_sKey);
        $ctr = 0;
        $tmp = '';
        for($i = 0; $i < strlen($p_sText); $i++){
            $ctr = $ctr == strlen($p_strKey) ? 0 : $ctr;
            $tmp .= $p_sText[$i] ^ $p_strKey[$ctr++];
        }
        return $tmp;
    }
}
?>